/* HTTP GET Example using plain POSIX sockets

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>

#include "common.h"
#include "http.h" //

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
// #include "protocol_examples_common.h"



#include "common.h"

#include "sdkconfig.h"


#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"

/* Constants that aren't configurable in menuconfig */
#define WEB_SERVER "192.168.1.100"
#define WEB_PORT "5001"
// #define WEB_PATH "/3D/agv/car"

// #define WEB_SERVER "192.168.1.1"
// #define WEB_PORT "80"

static const char *TAG = "line_http_request";



// static const char *http_body = "{\"action\":\"advance\"}";  // 示例JSON请求体
static const char *http_body = "";  // 示例JSON请求体

char REQUEST[256]; //只是装得下请求头的大小，不然需要增加数组大小

char *REQUEST_TEST = "POST /mes/agv-car HTTP/1.1\r\n"
"Host: 192.168.1.100:5000\r\n"
"User-Agent: Apifox/1.0.0 (https://apifox.com)\r\n"
"Content-Type: application/json\r\n"
"Accept: */*\r\n"
"Host: 192.168.1.100:5000\r\n"
"Connection: keep-alive\r\n"
"\r\n"
"{\"action\": \"advance\"}";

static int http_get_task(char *url, char *method, char *http_body, char *response, int response_size)
{
    
    // static const char *HEADER = "%s""" "%s"" HTTP/1.1\r\n"
    // "Host: "WEB_SERVER":"WEB_PORT"\r\n"
    // "Content-Type: application/json;charset=utf-8\r\n"
    // "\r\n"
    // "%s";

    static const char *HEADER = "%s %s HTTP/1.1\r\n"
    "Host: "WEB_SERVER":"WEB_PORT"\r\n"
    "Content-Type: application/json;charset=utf-8\r\n"
    "\r\n";

    const struct addrinfo hints = {
        .ai_family = AF_INET,
        .ai_socktype = SOCK_STREAM,
    };
    struct addrinfo *res;
    struct in_addr *addr;
    int s, r;
    char recv_buf[response_size-2]; //信息若不完整，需要增加数组大小

   // 使用sprintf填充完整的HTTP请求，包括计算内容长度
    sprintf(REQUEST, HEADER, method, url);
 
    memset(response, 0, response_size);
    
    // bzero(response, sizeof(response));

    int err = getaddrinfo(WEB_SERVER, WEB_PORT, &hints, &res);

    if(err != 0 || res == NULL) {
        ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        return ESP_FAIL;
    }

    /* Code to print the resolved IP.

        Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */
    addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
    ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*addr));

    s = socket(res->ai_family, res->ai_socktype, 0);
    if(s < 0) {
        ESP_LOGE(TAG, "... Failed to allocate socket.");
        freeaddrinfo(res);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        return ESP_FAIL;
    }
    ESP_LOGI(TAG, "... allocated socket");

    if(connect(s, res->ai_addr, res->ai_addrlen) != 0) {
        ESP_LOGE(TAG, "... socket connect failed errno=%d", errno);
        close(s);
        freeaddrinfo(res);
        vTaskDelay(4000 / portTICK_PERIOD_MS);
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "... connected");
    freeaddrinfo(res);

    if (write(s, REQUEST, strlen(REQUEST)) < 0) {
        ESP_LOGE(TAG, "... socket send failed");
        close(s);
        vTaskDelay(4000 / portTICK_PERIOD_MS);
        return ESP_FAIL;
    }
    // char *buffer = {"0"};
    // snprintf(buffer, sizeof(buffer), );
    ESP_LOGI(TAG, "... socket send success! url: %s", REQUEST);

    struct timeval receiving_timeout;
    receiving_timeout.tv_sec = 5;
    receiving_timeout.tv_usec = 0;
    if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &receiving_timeout,
            sizeof(receiving_timeout)) < 0) {
        ESP_LOGE(TAG, "... failed to set socket receiving timeout");
        close(s);
        vTaskDelay(4000 / portTICK_PERIOD_MS);
        return ESP_FAIL;
    }
    ESP_LOGI(TAG, "... set socket receiving timeout success");

    /* Read HTTP response */
    bool is_json = false;

    do {
        bzero(recv_buf, sizeof(recv_buf));
        r = read(s, recv_buf, sizeof(recv_buf)-1);
        // for(int i = 0; i < r; i++) {
        //     putchar(recv_buf[i]);
        // }
        for(int i = 0; i < r; i++) {
            if (recv_buf[i] == '{')
            {
                is_json = true;
                i = r;
            }
            else
            {
                putchar(recv_buf[i]);
                bzero(recv_buf, sizeof(recv_buf));
                is_json = false;
            }
        }
        // 如果需要，可以将数据复制到response中（确保response足够大）  
        if (r > 0 && r < response_size) {  
            strcpy(response, recv_buf);  
        } else if (r >= response_size) {  
            // response缓冲区太小，处理错误或截断数据  
            fprintf(stderr, "Response buffer is too small\n");  
            // ... 可以选择截断数据或者采取其他行动  
        } 
    } while(r > 0);

    ESP_LOGI(TAG, "... done reading from socket. Last read return=%d errno=%d.", r, errno);
    close(s);

    return ESP_OK;
}

int http_init(void)
{
    ESP_ERROR_CHECK(nvs_flash_init() );
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    return ESP_OK;
}

int http_process(char *url, char *method, char *data, char *response, int response_size)
{
    int ret = http_get_task(url, method, data, response, response_size);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "http_get_task error");
        // esp_restart();
        return ESP_FAIL;
    }
    return ESP_OK;
}

// int http_main(void)
// {



//     /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
//      * Read "Establishing Wi-Fi or Ethernet Connection" section in
//      * examples/protocols/README.md for more information about this function.
//      */

//     // while (1)
//     // {
//     //     http_get_task(NULL);
//     // }

//     return 0;
// }
